"""
认证装饰器模块
提供各种权限验证装饰器
"""
from functools import wraps
from flask import request, jsonify, current_app
from flask_login import current_user
from flask_jwt_extended import verify_jwt_in_request, get_jwt_identity, get_jwt
from app.models.user import User
from app.auth.exceptions import AuthenticationError, AuthorizationError, PermissionDeniedError

def login_required(f):
    """
    登录要求装饰器
    确保用户已登录才能访问
    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not current_user.is_authenticated:
            if request.is_json:
                return jsonify({
                    'error': '认证失败',
                    'message': '请先登录以访问此资源',
                    'code': 401
                }), 401
            from flask import redirect, url_for, flash
            flash('请先登录以访问此页面。', 'warning')
            return redirect(url_for('auth.login', next=request.url))
        return f(*args, **kwargs)
    return decorated_function

def admin_required(f):
    """
    管理员要求装饰器
    确保用户具有管理员权限
    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not current_user.is_authenticated:
            raise AuthenticationError('请先登录')
        
        if not current_user.is_admin:
            raise PermissionDeniedError('需要管理员权限')
        
        return f(*args, **kwargs)
    return decorated_function

def permission_required(permission):
    """
    权限要求装饰器
    确保用户具有特定权限
    
    Args:
        permission: 需要的权限字符串
    """
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not current_user.is_authenticated:
                raise AuthenticationError('请先登录')
            
            if not current_user.has_permission(permission):
                raise PermissionDeniedError(f'需要权限: {permission}')
            
            return f(*args, **kwargs)
        return decorated_function
    return decorator

def role_required(*roles):
    """
    角色要求装饰器
    确保用户具有指定角色之一
    
    Args:
        *roles: 允许的角色列表
    """
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not current_user.is_authenticated:
                raise AuthenticationError('请先登录')
            
            if current_user.role not in roles:
                role_names = ', '.join(roles)
                raise PermissionDeniedError(f'需要角色: {role_names}')
            
            return f(*args, **kwargs)
        return decorated_function
    return decorator

def jwt_required(optional=False, fresh=False, refresh=False):
    """
    JWT令牌要求装饰器
    扩展Flask-JWT-Extended的jwt_required装饰器
    
    Args:
        optional: 是否可选（令牌不存在时不报错）
        fresh: 是否需要新鲜令牌
        refresh: 是否是刷新令牌
    """
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            try:
                # 验证JWT令牌
                verify_jwt_in_request(optional=optional, fresh=fresh, refresh=refresh)
                
                # 获取当前用户
                user_id = get_jwt_identity()
                if user_id:
                    from app import db
                    user = User.query.get(user_id)
                    if not user or not user.is_active:
                        raise AuthenticationError('用户不存在或已被禁用')
                
                return f(*args, **kwargs)
                
            except Exception as e:
                if request.is_json:
                    return jsonify({
                        'error': '令牌验证失败',
                        'message': str(e),
                        'code': 401
                    }), 401
                raise AuthenticationError(str(e))
        
        return decorated_function
    return decorator

def api_key_required(f):
    """
    API密钥要求装饰器
    用于保护API端点
    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        api_key = request.headers.get('X-API-Key') or request.args.get('api_key')
        
        if not api_key:
            return jsonify({
                'error': 'API密钥缺失',
                'message': '请提供有效的API密钥',
                'code': 401
            }), 401
        
        # 验证API密钥（这里可以扩展为从数据库验证）
        valid_keys = current_app.config.get('API_KEYS', [])
        if api_key not in valid_keys:
            return jsonify({
                'error': '无效的API密钥',
                'message': '提供的API密钥无效',
                'code': 401
            }), 401
        
        return f(*args, **kwargs)
    return decorated_function

def rate_limit(requests_per_minute=60, key_func=None):
    """
    速率限制装饰器
    防止API滥用
    
    Args:
        requests_per_minute: 每分钟允许的请求数
        key_func: 用于生成限制键的函数
    """
    from collections import defaultdict
    from time import time
    import functools
    
    # 简单的内存存储（生产环境应使用Redis）
    request_counts = defaultdict(list)
    
    def get_default_key():
        """默认的速率限制键（基于IP地址）"""
        return request.remote_addr
    
    key_func = key_func or get_default_key
    
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            key = key_func()
            now = time()
            
            # 清理过期的请求记录
            request_counts[key] = [t for t in request_counts[key] if now - t < 60]
            
            # 检查是否超过限制
            if len(request_counts[key]) >= requests_per_minute:
                return jsonify({
                    'error': '请求过于频繁',
                    'message': f'每分钟最多允许{requests_per_minute}次请求',
                    'code': 429
                }), 429
            
            # 记录本次请求
            request_counts[key].append(now)
            
            return f(*args, **kwargs)
        return decorated_function
    return decorator

def validate_schema(schema_class):
    """
    请求数据验证装饰器
    使用marshmallow schema验证输入数据
    
    Args:
        schema_class: marshmallow Schema类
    """
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            schema = schema_class()
            
            # 根据请求方法获取数据
            if request.method in ['POST', 'PUT', 'PATCH']:
                if request.is_json:
                    data = request.get_json()
                else:
                    data = request.form.to_dict()
            else:
                data = request.args.to_dict()
            
            # 验证数据
            errors = schema.validate(data)
            if errors:
                return jsonify({
                    'error': '数据验证失败',
                    'message': '输入数据不符合要求',
                    'details': errors,
                    'code': 400
                }), 400
            
            # 将验证后的数据添加到kwargs中
            kwargs['validated_data'] = schema.load(data)
            
            return f(*args, **kwargs)
        return decorated_function
    return decorator

def cache_control(max_age=300, s_maxage=300, public=True):
    """
    缓存控制装饰器
    设置HTTP缓存头
    
    Args:
        max_age: 浏览器缓存时间（秒）
        s_maxage: 代理服务器缓存时间（秒）
        public: 是否公开缓存
    """
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            response = f(*args, **kwargs)
            
            if hasattr(response, 'headers'):
                cache_control = []
                if public:
                    cache_control.append('public')
                else:
                    cache_control.append('private')
                
                cache_control.extend([
                    f'max-age={max_age}',
                    f's-maxage={s_maxage}'
                ])
                
                response.headers['Cache-Control'] = ', '.join(cache_control)
                response.headers['Expires'] = f'{max_age}'
            
            return response
        return decorated_function
    return decorator

def handle_exceptions(f):
    """
    异常处理装饰器
    统一处理视图函数中的异常
    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except AuthenticationError as e:
            return jsonify({
                'error': '认证失败',
                'message': str(e),
                'code': 401
            }), 401
        except AuthorizationError as e:
            return jsonify({
                'error': '授权失败',
                'message': str(e),
                'code': 403
            }), 403
        except PermissionDeniedError as e:
            return jsonify({
                'error': '权限不足',
                'message': str(e),
                'code': 403
            }), 403
        except Exception as e:
            current_app.logger.error(f'未处理的异常: {str(e)}', exc_info=True)
            return jsonify({
                'error': '服务器内部错误',
                'message': '服务器遇到意外错误',
                'code': 500
            }), 500
    return decorated_function